/**
* A force directed graph layout implementation by liuchang on 2018/05/10.
*/

let k;


let mDxMap = {};
let mDyMap = {};
let mNodeMap = {};


export function ForceDirected (CANVAS_WIDTH, CANVAS_HEIGHT, mNodeList, mEdgeList) {




    k = Math.sqrt(CANVAS_WIDTH * CANVAS_HEIGHT / mNodeList.length);

    for (let i = 0; i < mNodeList.length; i++) {
        let node = mNodeList[i];
        if (node) {
            mNodeMap[node.id] = node;
        }
    }

    //随机生成坐标. Generate coordinates randomly.
    let initialX, initialY, initialSize = 40.0 * 5;
    for (let i in mNodeList) {
        initialX = CANVAS_WIDTH * .5;
        initialY = CANVAS_HEIGHT * .5;
        mNodeList[i].x = initialX + initialSize * (Math.random() - .5);
        mNodeList[i].y = initialY + initialSize * (Math.random() - .5);
    }

    //迭代200次. Iterate 200 times.
    for (let i = 0; i < 200; i++) {
        calculateRepulsive(mNodeList);
        calculateTraction(mEdgeList);
        updateCoordinates(mNodeList, CANVAS_WIDTH, CANVAS_HEIGHT);
    }

    return mNodeList;
    //console.log();

}



/**
* 计算两个Node的斥力产生的单位位移。
* Calculate the displacement generated by the repulsive force between two nodes.*
*/
function calculateRepulsive (mNodeList) {
    let ejectFactor = 6 * 5;
    let distX, distY, dist;
    for (let i = 0; i < mNodeList.length; i++) {
        mDxMap[mNodeList[i].id] = 0.0;
        mDyMap[mNodeList[i].id] = 0.0;
        for (let j = 0; j < mNodeList.length; j++) {
            if (i !== j) {
                distX = mNodeList[i].x - mNodeList[j].x;
                distY = mNodeList[i].y - mNodeList[j].y;
                dist = Math.sqrt(distX * distX + distY * distY);
            }
            if (dist < 30 * 5) {
                ejectFactor = 5 * 5;
            }
            if (dist > 0 && dist < 250 * 5) {
                let id = mNodeList[i].id;
                mDxMap[id] = mDxMap[id] + distX / dist * k * k / dist * ejectFactor;
                mDyMap[id] = mDyMap[id] + distY / dist * k * k / dist * ejectFactor;
            }
        }
    }
}

/**
* 计算Edge的引力对两端Node产生的引力。
* Calculate the traction force generated by the edge acted on the two nodes of its two ends.
*/
function calculateTraction (mEdgeList) {
    let condenseFactor = 3 * 5;
    let startNode, endNode;
    for (let e = 0; e < mEdgeList.length; e++) {
        let eStartID = mEdgeList[e].source;
        let eEndID = mEdgeList[e].target;
        startNode = mNodeMap[eStartID];
        endNode = mNodeMap[eEndID];
        if (!startNode) {
            return;
        }
        if (!endNode) {
            return;
        }
        let distX, distY, dist;
        distX = startNode.x - endNode.x;
        distY = startNode.y - endNode.y;
        dist = Math.sqrt(distX * distX + distY * distY);
        mDxMap[eStartID] = mDxMap[eStartID] - distX * dist / k * condenseFactor;
        mDyMap[eStartID] = mDyMap[eStartID] - distY * dist / k * condenseFactor;
        mDxMap[eEndID] = mDxMap[eEndID] + distX * dist / k * condenseFactor;
        mDyMap[eEndID] = mDyMap[eEndID] + distY * dist / k * condenseFactor;
    }
}

/**
* 更新坐标。
* update the coordinates.
*/
function updateCoordinates (mNodeList, CANVAS_WIDTH, CANVAS_HEIGHT) {
    let maxt = 4, maxty = 3; //Additional coefficients.
    for (let v = 0; v < mNodeList.length; v++) {
        let node = mNodeList[v];
        let dx = Math.floor(mDxMap[node.id]);
        let dy = Math.floor(mDyMap[node.id]);

        if (dx < -maxt) dx = -maxt;
        if (dx > maxt) dx = maxt;
        if (dy < -maxty) dy = -maxty;
        if (dy > maxty) dy = maxty;
        node.x = node.x + dx >= CANVAS_WIDTH || node.x + dx <= 0 ? node.x - dx : node.x + dx;
        node.y = node.y + dy >= CANVAS_HEIGHT || node.y + dy <= 0 ? node.y - dy : node.y + dy;
    }
}




